# ----------------------------------------------------------------------------
#	(C) COPYRIGHT HiPi, 2014
#
#	FILE:
#		denon_avr_serial.py
#
#	DESCRIPTION:
#       This file contains the AVE Serial class
#       This uses a GPIO serial port to communicate with devices
#
#	REFERENCES:
#		TODO
#
#   $Author: VNG $
#
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# Imported Modules
# ----------------------------------------------------------------------------
import logging
import serial
import threading
import usbenum
import time

# ----------------------------------------------------------------------------
# CLASS:
#   SLFSerial
#
# DESCRIPTION:
#   TODO
#
# ----------------------------------------------------------------------------
class SLFSerial(object):

    # ---------------------------------------------------------- 
                   
    # ----------------------------------------------------------
    def __init__(self):
                               
        self._ser   = serial.Serial()        
        self._lock  = threading.Lock()

    # ----------------------------------------------------------
    def find_device(self):
        dev = usbenum.find_device(self.CFG_USB_DEVID)
        if dev:
            logging.info("Detected USB device: %s", dev)
            return dev[0]
        raise IOError("Failed to find USB device")

    # ----------------------------------------------------------
    def connect(self,
                port=CFG_DEFAULT_COM_PORT,
                baud=CFG_DEFAULT_BAUDRATE,
                timeout=CFG_DEFAULT_TIMEOUT):

        self._ser.baudrate = baud
        self._ser.timeout = timeout

        if port == 'AUTO':
            self._ser.port = self.find_device()
        else:
            self._ser.port = port

        logging.info("Using COM port: %s", self._ser.port)
        self._ser.open()

    # ----------------------------------------------------------
    def set_read_timeout(self, timeout):

        self._ser.timeout = timeout

    # ----------------------------------------------------------
    def get_read_timeout(self):

        return self._ser.timeout

    # ----------------------------------------------------------
    def disconnect(self):

        try:            
            self._ser.timeout = 0.1
            if self.is_connected():           
                self._ser.close()
        except:
            pass

    # ----------------------------------------------------------
    def is_connected(self):

        self._lock.acquire()
        connected = self._ser.isOpen()
        self._lock.release()
        
        return connected

    # ----------------------------------------------------------
    def write_byte(self, byte):

        self.write_bytes(byte)

    # ----------------------------------------------------------
    def write_bytes(self, wbytes):

        self._lock.acquire()
        self._ser.write(wbytes)
        self._ser.flush()
        self._lock.release()

    # ----------------------------------------------------------
    def write(self, s):

        self._lock.acquire()
        self._ser.write(s)
        self._ser.flush()
        self._lock.release()

    # ----------------------------------------------------------
    def bytes_available(self):

        self._lock.acquire()
        count = self._ser.inWaiting()
        self._lock.release()

        return count

    # ----------------------------------------------------------
    def read_byte(self):
    
        return self.read_bytes(1)

    # ----------------------------------------------------------
    def read_bytes(self, size):

        # wait until there are enough bytes in the receive
        # buffer, if not wait here. this is an ultra cautious
        # approach, it prevents writes occurring whilst we
        # are inside the call to read.
        while self.bytes_available() < size:
            time.sleep(0.001)

        # read the bytes from the buffer
        self._lock.acquire()
        bytes = self._ser.read(size)
        self._lock.release()

        # return the bytes read
        return bytes

    # ----------------------------------------------------------
    def flush_input(self, wait_time=0.0):

        if wait_time > 0:
            time.sleep(wait_time)

        self._lock.acquire()
        self._ser.flushInput()
        self._lock.release()
                        
# ------------------------------------------------------------------------------------
# End Of File
# ------------------------------------------------------------------------------------
